﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEngine;
using Object = System.Object;

namespace UFrame
{
    public static class RefPrint
    {
        public static PrintLog PrintTool = new UnityPrintLog();

        private static StringBuilder sb = new StringBuilder();

        public static void PrintClassInfo<T>(T obj) where T : class
        {
            if (obj == null)
            {
                PrintTool.WriteLine("print class of null");
                return;
            }

            sb = new StringBuilder();
            sb.AppendLine(obj.GetType().Name);
            Type t = obj.GetType();
            PrintFields(t, obj);
            PrintTool.WriteLine(sb.ToString());
        }

        public static void PrintClassInfo(object obj)
        {
            if (obj == null)
            {
                PrintTool.WriteLine("print class of null");
                return;
            }

            sb = new StringBuilder();
            sb.AppendLine(obj.GetType().Name);
            Type t = obj.GetType();
            PrintFields(t, obj);
            PrintTool.WriteLine(sb.ToString());
        }

        public static void PrintClassInfo(object obj, string path)
        {
            if (obj == null)
            {
                PrintTool.WriteLine("print class of null");
                return;
            }

            sb = new StringBuilder();
            string[] strList = path.Split('.');

            Type t = obj.GetType();
            FieldInfo f = null;

            for (int i = 0; i < strList.Length; i++)
            {
                f = GetField(t, strList[i]);


                if (i == strList.Length - 1)
                {
                    if (f != null)
                    {
                        sb.AppendLine(f.GetValue(obj).ToString());
                    }

                    return;
                }

                t = f.GetType();
            }


            PrintTool.WriteLine(sb.ToString());
        }

        public static FieldInfo GetField(Type type, string findField)
        {
            var fList = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            foreach (var item in fList)
            {
                if (item.FieldType.Name.Equals(findField))
                {
                    return item;
                }
            }

            return null;
        }


        public static void PrintList(object listValue)
        {
            IEnumerable e = listValue as IEnumerable;
            FieldInfo[] fList = null;
            int count = 0;
            foreach (var c in e)
            {
                fList = c.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
                foreach (var item in fList)
                {
                    sb.AppendLine(item.FieldType.Name + "\t" + item.Name + "\t" + item.GetValue(c).ToString());
                }

                count++;
            }

            sb.AppendLine("个数:" + count);
        }

        public static void PrintAry(object aryValue)
        {
            IEnumerable e = aryValue as IEnumerable;
            FieldInfo[] fList = null;
            int count = 0;
            foreach (var c in e)
            {
                if (c.GetType().Name.Contains("String"))
                {
                    sb.AppendLine(c.GetType().Name + "\t" + c);
                }
                else if (!c.GetType().Name.Contains("String") && c.GetType().IsClass)
                {
                    fList = c.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
                    foreach (var item in fList)
                    {
                        sb.AppendLine(item.FieldType.Name + "\t" + item.Name + "\t" + item.GetValue(c));
                    }
                }
                else
                {
                    sb.AppendLine(c.GetType().Name + "\t" + c + "\t");
                }

                count++;
            }

            sb.AppendLine("个数:" + count);
        }

        public static void PrintDictionary(object dirValue)
        {
            IDictionary e = dirValue as IDictionary;
            FieldInfo[] fList = null;
            int count = 0;
            foreach (var c in e.Values)
            {
                fList = c.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
                foreach (var item in fList)
                {
                    sb.AppendLine(item.FieldType.Name + "\t" + item.Name + "\t" + item.GetValue(c).ToString());
                }

                count++;
            }

            sb.AppendLine("个数:" + count);
        }

        public static void PrintMemebers(Type t, object value)
        {
            var mList = t.GetMembers();

            foreach (var item in mList)
            {
                sb.AppendLine("method: " + item.Name);
            }
        }

        public static void PrintFields(Type t, object value)
        {
            var fList = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            foreach (var item in fList)
            {
                if (!item.FieldType.IsClass || (item.FieldType.Name == "String"))
                {
                    sb.AppendLine(item.FieldType.Name + "\t" + item.Name + "\t" + item.GetValue(value));
                    continue;
                }
                else if (item.FieldType.Name.Contains("List"))
                {
                    sb.AppendLine("集合" + item.Name + ":");
                    Object listValue = item.GetValue(value);
                    PrintList(listValue);
                    continue;
                }
                else if (item.FieldType.Name.Contains("[]"))
                {
                    sb.AppendLine("数组" + item.Name + ":");
                    Object aryValue = item.GetValue(value);
                    PrintAry(aryValue);
                    continue;
                }
                else if (item.FieldType.Name.Contains("Dictionary"))
                {
                    sb.AppendLine("字典" + item.Name + ":");
                    Object aryValue = item.GetValue(value);


                    PrintDictionary(aryValue);
                    continue;
                }
                else
                {
                    sb.AppendLine(item.FieldType.Name + "\t" + item.Name + "\t" + item.GetValue(value));
                    continue;
                }
            }
        }

        public static void PrintProperties(Type t, object value)
        {
            var pList = t.GetProperties();

            foreach (var item in pList)
            {
                sb.AppendLine(item.Name + " " + item.GetValue(value, new object[] { }));
            }
        }
    }

    public interface PrintLog
    {
        void WriteLine(string text);
    }

    public class UnityPrintLog : PrintLog
    {
        public void WriteLine(string text)
        {
            Debug.Log(text);
        }
    }


    public static class RefStatic
    {
        /*public static FieldInfo RefSetFieldVal(this object t, object val)
        {
            Type type = t.GetType();
            FieldInfo i = type.GetField(t.GetType().Name);
            i.SetValue(t,val);
            return i;
        }*/

        private static FieldInfo RefField(this object t, string name, bool searchUp = false)
        {
            FieldInfo info = SearchField(t, name, searchUp);
            return info;
        }

        public static Object RefGetFieldVal(this object t, string name, bool searchUp = false)
        {
            FieldInfo info = SearchField(t, name, searchUp);
            return info.GetValue(t);
        }

        public static FieldInfo RefSetFieldVal(this object t, string name, object val, bool searchUp = false)
        {
            FieldInfo info = SearchField(t, name, searchUp);
            if (info != null)
            {
                info.SetValue(t, val);
            }

            return info;
        }

        public static FieldInfo RefStaticField(this object t, string name, bool searchUp = false)
        {
            FieldInfo info = t.GetType().GetField(name, BindingFlags.Static | BindingFlags.NonPublic);
            return info;
        }

        public static FieldInfo RefSetStaticFieldVal(this object t, string name, object val, bool searchUp = false)
        {
            FieldInfo info = t.GetType().GetField(name, BindingFlags.Static | BindingFlags.NonPublic);
            if (info != null)
            {
                info.SetValue(t, val);
            }

            return info;
        }


        public static PropertyInfo RefSetPropertyVal(this object t, string name, object val)
        {
            PropertyInfo info = t.GetType().GetProperty(name);
            if (info != null)
            {
                info.SetValue(t, val, null);
            }

            return info;
        }

        public static object RefExecuteMethod(this object t, string name, object[] parameters = null)
        {
            MethodInfo info = t.GetType()
                .GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
            if (info != null)
            {
                return info.Invoke(t, parameters);
            }

            return null;
        }

        private static FieldInfo SearchField(object t, string name, bool parentSearch)
        {
            FieldInfo info = null;
            if (parentSearch)
            {
                Type type = t.GetType();
                while (info == null && type != null)
                {
                    info = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                    type = type.BaseType;
                }
            }
            else
            {
                info = t.GetType().GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            }

            return info;
        }
    }

    public class RefTools
    {
        public static bool IsHasAttribute<T>(Type type) where T : class
        {
            Object[] oList = type.GetCustomAttributes(typeof(T), false);
            foreach (var item in oList)
            {
                if ((item as T) != null)
                {
                    return true;
                }
            }

            return false;
        }

        public static bool IsSubClass<T>(Type type)
        {
            return typeof(T).IsAssignableFrom(type);
        }

        public static bool IsContainAttribute<T>(FieldInfo type)
        {
            var list = type.GetCustomAttributes(false);
            for (int i = 0; i < list.Length; i++)
            {
                if (list[i] is T)
                {
                    return true;
                }
            }

            return false;
        }

        public static List<FieldInfo> GetFieldParentAry<T>(Type t) where T : class
        {
            List<FieldInfo> fieldInfoList = new List<FieldInfo>();
            var i = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            FieldInfo[] infoAry = t.GetFields(i);
            fieldInfoList.AddRange(infoAry);
            Type baseType = t.BaseType;
            while (baseType != null)
            {
                infoAry = baseType.GetFields(i);
                fieldInfoList.AddRange(infoAry);
                baseType = baseType.BaseType;
                if (baseType.FullName == typeof(T).FullName)
                {
                    break;
                }
            }

            return fieldInfoList;
        }

        public static void SetFieldValue(FieldInfo item, object t, object objVal)
        {
            string name = item.FieldType.Name;
            var genericType = item.FieldType.GenericTypeArguments;
            if (genericType.Length > 0)
            {
                name = genericType[0].Name;
            }

            if (name == "Int32")
            {
                item.SetValue(t, Convert.ToInt32(objVal));
            }
            else if (name == "Float")
            {
                item.SetValue(t, Convert.ToSingle(objVal));
            }
            else if (name == "Single")
            {
                item.SetValue(t, Convert.ToSingle(objVal));
            }
            else if (name == "Double")
            {
                item.SetValue(t, Convert.ToDouble(objVal));
            }
            else if (name == "Boolean")
            {
                item.SetValue(t, Convert.ToBoolean(objVal));
            }
            else if (name == "DBNull")
            {
                item.SetValue(t, null);
            }
            else if (name == "String")
            {
                item.SetValue(t, objVal.ToString());
            }
            else if (name == "Nullable`1")
            {
                item.SetValue(t, objVal.ToString());
            }
            else
            {
                throw new ArgumentException("不存在的常用类型" + item.FieldType.Name);
            }
        }

        public static bool IsEquality<T>(T a, T b) where T : new()
        {
            var typeA = typeof(T);

            var fieldAAry = typeA.GetFields(BindingFlags.Instance | BindingFlags.Public);

            bool result = true;
            foreach (var fieldInfo in fieldAAry)
            {
                if (fieldInfo.GetValue(a) != fieldInfo.GetValue(b))
                {
                    result = false;
                    break;
                }
            }

            return result;
        }


    }
}